#pragma once

#include <string>
#include <sstream>
#include <vector>
#include <map>
#include <set>



template<typename...>
using void_t = void;

template<typename T, typename = void>
struct has_value_type : std::false_type {};
template<typename T> struct has_value_type<T, void_t<typename T::value_type>> : std::true_type {};

template<typename T>
struct is_string : std::false_type {};
template<> struct is_string<std::string> : std::true_type {};

template <typename T>
struct is_container
{
private:
	template<typename U>
	static auto test(int) -> decltype(
		std::declval<U>().begin(),  // 检测函数是否存在
		std::declval<U>().end(),  // 检测函数是否存在
		std::true_type()
		);
	template<typename>
	static std::false_type test(...);
public:
	static constexpr bool value = decltype(test<T>(0))::value && has_value_type<T>::value;
};

template <typename Container, typename = void>
struct has_kv : std::false_type {};

template <typename Container>
struct has_kv<Container,
	typename std::enable_if<std::is_same<typename Container::value_type,
	std::pair<const typename Container::key_type, typename Container::mapped_type>>::value>::type>
	: std::true_type {};

template <typename T>
struct has_to_string
{
private:
	template<typename U>
	static auto test(int) -> decltype(
		std::declval<U>().ToString(),  // 检测函数是否存在
		std::true_type()
		);
	template<typename>
	static std::false_type test(...);
public:
	static constexpr bool value = decltype(test<T>(0))::value;
};

struct TagOutput_Basic {};
struct TagOutput_ByToString {};
struct TagOutput_Container {};
struct TagOutput_ContainerKV {};

template<typename T>
auto gen_output_tag() -> typename std::conditional<is_container<T>::value,
	typename std::conditional<is_string<T>::value, TagOutput_Basic,
	typename std::conditional<has_kv<T>::value, TagOutput_ContainerKV, TagOutput_Container>::type>::type,
	typename std::conditional<has_to_string<T>::value, TagOutput_ByToString, TagOutput_Basic>::type
>::type {
	return {};
}

template <typename T> inline void Output(std::stringstream& ss, const char* name, const T& t);

template <typename T> inline void OutputImpl(std::stringstream& ss, const T& t, TagOutput_Basic)
{
	ss << t;
}
template <typename T> inline void OutputImpl(std::stringstream& ss, const T& t, TagOutput_ByToString)
{
	ss << t.ToString();
}
template <typename T> inline void OutputImpl(std::stringstream& ss, const T& t, TagOutput_Container)
{
	bool init = false;
	ss << "[";
	for (auto& item : t)
	{
		if (init)ss << ","; else init = true;
		OutputImpl(ss, item, gen_output_tag<typename T::value_type>());
	}
	ss << "]";
}
template <typename T> inline void OutputImpl(std::stringstream& ss, const T& t, TagOutput_ContainerKV)
{
	bool init = false;
	ss << "{";
	for (auto& item : t)
	{
		if (init)ss << ","; else init = true;
		OutputImpl(ss, item.first, gen_output_tag<typename T::key_type>());
		ss << ":";
		OutputImpl(ss, item.second, gen_output_tag<typename T::mapped_type>());
	}
	ss << "}";
}


template <typename T> inline void Output(std::stringstream& ss, const char* name, const T& t)
{
	ss << name << ":";
	return OutputImpl(ss, t, gen_output_tag<T>());
}

struct TagClean_Basic {};
struct TagClean_HasClear {};

template <typename T>
struct has_clear
{
private:
	template<typename U>
	static auto test(int) -> decltype(
		std::declval<U>().clear(),  // 检测函数是否存在
		std::true_type()
		);
	template<typename>
	static std::false_type test(...);
public:
	static constexpr bool value = decltype(test<T>(0))::value;
};

template<typename T>
auto gen_clean_tag() -> typename std::conditional<has_clear<T>::value, TagClean_HasClear, TagClean_Basic>::type {
	return {};
}

template <typename T> inline void CleanVar(T& t);
template <typename T> inline void CleanImpl(T& t, TagClean_Basic) 
{ 
  t = 0;
}
template <typename T> inline void CleanImpl(T& t, TagClean_HasClear) 
{ 
  t.clear();
}
template <typename T> inline void CleanVar(T& t) 
{
  return CleanImpl(t, gen_clean_tag<T>());
}

#define ST_DATA_DEFINE(n, s) n s;
#define ST_DATA_RESET(n, s) CleanVar(s);
#define ST_DATA_COPY(n, s) this->s = that.s;
#define ST_DATA_MOVE(n, s) this->s = std::move(that.s);
#define ST_DATA_OUTPUT(n, s) if(init)str<<",";else init=true; Output(str, #s, s); 

#define ST_STRUCT_CALL(m, f) m(f)
#define ST_STRUCT_GEN(c, m)                                                                         \
struct c {                                                                                          \
    ST_STRUCT_CALL(m, ST_DATA_DEFINE)                                                               \
    inline void Reset() { ST_STRUCT_CALL(m, ST_DATA_RESET); }                                       \
    inline c() { ST_STRUCT_CALL(m, ST_DATA_RESET); }                                                \
    inline c(const c& that) { ST_STRUCT_CALL(m, ST_DATA_COPY); }                                    \
    inline c(c&& that) { ST_STRUCT_CALL(m, ST_DATA_MOVE); }                                         \
    inline c& operator=(const c& that) { ST_STRUCT_CALL(m, ST_DATA_COPY); return *this; }           \
    inline c& operator=(c&& that) { ST_STRUCT_CALL(m, ST_DATA_MOVE); return *this; }                \
    inline std::string ToString() const { std::stringstream str; bool init = false; ST_STRUCT_CALL(m, ST_DATA_OUTPUT); return str.str(); };  \
};   \
template <> inline void CleanVar<c>(c& t) { t.Reset(); }    \
template <> inline void Output<c>(std::stringstream& ss, const char* name, const c& t) { ss << "{" << name << ":" << t.ToString() << "}"; }


/*
demo:

// #define ST_HTTP_REQUEST_VAR(XX)                                                    \
//   XX(std::string, method)                                                       \
//   XX(std::string, url)                                                          \
//   XX(stHttpHeadDic, heads)                                                      \
//   XX(std::string, body)                                                         
// ST_STRUCT_GEN(stHttpRequest, ST_HTTP_REQUEST_VAR)
*/


